#ifndef CMA_H
#define	CMA_H

#include "Individual.h"
#include <vector>
#include "../LapackPlatform/Lapack.h"
#include "Model.h"

namespace learn {
	
struct NormalRandom {
	NormalRandom();
	
	void seed(long s);	
	float next();	
	float next(float mean, float stdDeviation);
	
private:
	float values[2];
	int current;
	unsigned sx, sy, sz;
	
	void getNormalRandom(float &outRandom1, float &outRandom2);	
	double nextDouble();	
};

static NormalRandom normalRandom;
	
class CMA {
public:
	typedef enum { equal, linear, superlinear } RecombType;
	typedef enum { rankone, rankmu }            UpdateType;
		
	static int suggestLambda(int parameterCount);
	static int suggestMu(int lambda, RecombType recomb);
	
	virtual ~CMA();	
	
	void createIndividual(float *outParams);
	void init(int numParameters, float dispersion, float _sigma,
		Individual *population, size_t populationSize,
		RecombType recombination, 
		UpdateType update);
	void updateStrategyParameters(learn::Individual *population, size_t modelCount, float lowerBound = 0);
	
private:
	int n;
	float sigma;
	float chi_n;
	float cc;
	float cs;
	float csu;
	float ccu;
	float ccov;
	float d;
	float mueff;
	float mucov;
	
	std::vector<float> z;
	std::vector<float> pc;
	std::vector<float> ps;
	std::vector<float> C;
	std::vector<float> Z;
	std::vector<float> lambda;
	std::vector<float> B;
	std::vector<float> w;
	std::vector<float> theVector;
	
	std::vector<float> x; // center of gravity
	std::vector<float> xPrime;
	std::vector<float> randomMean;
	
	// for eigenvalues
	Lapack lapack;
	std::vector<int> ifail;
	std::vector<float> work;
	std::vector<int> iwork;
	
	void getCenterOfGravity(float *cog, learn::Individual *population, size_t populationSize);
	void getEigens(float *matrix, float *outEigenvectors, float *outEigenvalues);
	void allocateMemoryForEigens();	
};

}

#endif	/* CMA_H */

